package com.myzhouye.biz.utils;/***
 *                    _ooOoo_
 *                   o8888888o
 *                   88" . "88
 *                   (| -_- |)
 *                    O\ = /O
 *                ____/`---'\____
 *              .   ' \\| |// `.
 *               / \\||| : |||// \
 *             / _||||| -:- |||||- \
 *               | | \\\ - /// | |
 *             | \_| ''\---/'' | |
 *              \ .-\__ `-` ___/-. /
 *           ___`. .' /--.--\ `. . __
 *        ."" '< `.___\_<|>_/___.' >'"".
 *       | | : `- \`.;`\ _ /`;.`/ - ` : | |
 *         \ \ `-. \_ __\ /__ _/ .-` / /
 * ======`-.____`-.___\_____/___.-`____.-'======
 *                    `=---='
 *
 * .............................................
 *          佛祖保佑             永无BUG
 */

import com.myzhouye.biz.bean.LoginUser;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.ReactiveSecurityContextHolder;
import org.springframework.security.core.context.SecurityContext;
import reactor.core.publisher.Mono;

import java.time.Duration;

/**
 * @program: game01
 * @description: 安全工具类 - 获取当前登录用户信息
 * @author: 那条蠢鱼
 * @create: 2025-11-11 17:02
 **/
@Slf4j
public class SecurityUtils {

    /**
     * 获取当前登录用户信息
     * @return Mono<LoginUser> 登录用户信息，如果未登录返回空Mono
     */
    public static Mono<LoginUser> getCurrentUser() {
        return ReactiveSecurityContextHolder.getContext()
                .map(SecurityContext::getAuthentication)
                .doOnNext(auth -> log.info("获取到认证对象: {}，类型: {}", auth, auth.getClass().getSimpleName())) // 新增日志
                .filter(authentication -> {
                    boolean isAuthenticated = authentication != null
                            && !(authentication instanceof AnonymousAuthenticationToken)
                            && authentication.isAuthenticated();
                    log.info("认证状态: {}，是否为匿名用户: {}", isAuthenticated, authentication instanceof AnonymousAuthenticationToken); // 新增日志
                    return isAuthenticated;
                })
                .map(Authentication::getPrincipal)
                .doOnNext(principal -> log.info("Principal类型: {}，值: {}", principal.getClass().getSimpleName(), principal)) // 新增日志
                .cast(LoginUser.class)
                .doOnError(e -> log.error("提取LoginUser失败", e)); // 新增异常日志
    }

    /**
     * 获取当前登录用户ID
     * @return Mono<Long> 用户ID，如果未登录抛出异常
     */
    public static Mono<Long> getCurrentUserId() {
        return getCurrentUser()
                .map(LoginUser::getUserId)
                .switchIfEmpty(Mono.error(() -> {
                    return new IllegalStateException("用户未登录或登录状态已过期");
                }));
    }

    /**
     * 获取当前登录用户ID（阻塞方式）
     * <p>【注意】仅允许在非响应式环境（如 @Service 层普通方法）中使用，禁止在 WebFlux 控制器/响应式流中调用！</p>
     *
     * 尽量考虑使用非阻塞式的响应式流，避免阻塞线程。示例：
     * // 服务层方法定义
     * public void processUserTask(Long userId, Task task) { ... }
     *
     * // 控制器调用
     * @GetMapping("task")
     * public Mono<Void> handleTask() {
     *     return SecurityUtils.getCurrentUserId()
     *             .flatMap(userId -> taskService.processUserTask(userId, new Task()));
     * }
     *
     * @return Long 用户ID，未登录/阻塞失败返回 null
     */
    public static Long getCurrentUserIdOrNull() {
        try {
            // 尝试阻塞获取用户ID，设置超时时间避免无限阻塞
            return getCurrentUserId().block(Duration.ofSeconds(3));
        } catch (Exception e) {
            log.error("阻塞获取用户ID失败（可能在响应式线程中调用）", e);
            return null;
        }
    }

    /**
     * 检查用户是否已登录
     * @return Mono<Boolean> true=已登录，false=未登录
     */
    public static Mono<Boolean> isLoggedIn() {
        return getCurrentUser()
                .hasElement()
                .defaultIfEmpty(false);
    }
}
